package com.tsd.shiro.realm;

import com.tsd.core.datasource.DataSourceContextHolder;
import com.tsd.core.shiro.IShiroConst;
import com.tsd.core.shiro.config.ShiroAuthToken;
import com.tsd.core.shiro.helper.ShiroAuthorizationInfoHelper;
import com.tsd.core.utils.HlpUtils;
import com.tsd.core.vo.HlpException;
import com.tsd.jwt.JwtRefreshUtil;
import com.tsd.system.entity.SysRoleExt;
import com.tsd.system.entity.SysUserExt;
import com.tsd.system.service.SysResourceService;
import com.tsd.system.service.SysRoleService;
import com.tsd.system.utils.SysUtil;
import io.jsonwebtoken.Claims;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationException;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.annotation.Resource;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * @ClassName: TokenRealm
 * @Description: TODO
 * @Author: Hillpool
 * @Date: 2022/7/15 9:57
 * @Version: 1.0
 */
public class TokenRealm extends AuthorizingRealm {

    private static final Logger LOGGER = LoggerFactory.getLogger(TokenRealm.class);

    @Resource
    private SysRoleService sysRoleService;
    @Resource
    private SysResourceService sysResourceService;

    @Override
    public String getName() {
        return IShiroConst.REALM_NAME_TOKEN;
    }

    /**
     * 登录认证
     *
     * @param token
     * @return
     * @see SimpleAuthorizationInfo
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        ShiroAuthToken authToken = (ShiroAuthToken) token;
        LOGGER.info("登录认证，登录类型：{}，账套：{}", authToken.getLogin_type(), DataSourceContextHolder.getDataSourceType());
        if (HlpUtils.isEmpty(authToken.getToken()) && HlpUtils.isEmpty(authToken.getRefresh_token())) {
            throw new IncorrectCredentialsException("令牌不合法或者已过期，请重新登录");
        }
        try {
            Claims claims = JwtRefreshUtil.getToken(authToken.getToken());
            if (claims == null && !HlpUtils.isEmpty(authToken.getRefresh_token())) {
                claims = JwtRefreshUtil.getToken(authToken.getRefresh_token());
            }
            if (claims == null) {
                throw new HlpException("令牌不合法或者已过期，请重新登录");
            }
            SysUserExt userExt = SysUtil.getSysUserFromClaims(claims);
            return new SimpleAuthenticationInfo(userExt, userExt, this.getName());
        } catch (Exception e) {
            throw new AuthenticationException(e.getMessage());
        }
    }

    /**
     * 权限认证
     *
     * @param principals
     * @return
     * @see SimpleAuthorizationInfo
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        if (principals == null) {
            throw new AuthorizationException("PrincipalCollection method argument cannot be null.");
        }
        SysUserExt userExt = (SysUserExt) super.getAvailablePrincipal(principals);
        try {
            String cacheName = this.getName() + "_" + userExt.getSid();
            SimpleAuthorizationInfo info = ShiroAuthorizationInfoHelper.get(cacheName);
            if (info == null) {
                // 查询用户角色
                List<SysRoleExt> roleList = sysRoleService.query4ShiroAuthorizationInfo(userExt);
                Set<String> roles = new HashSet<>();
                if (!HlpUtils.isEmptyList(roleList)) {
                    for (SysRoleExt roleExt : roleList) {
                        if (!HlpUtils.isEmpty(roleExt.getCode())) {
                            roles.add(roleExt.getCode());
                        }
                    }
                }
                // 查询用户权限标识
                Set<String> permissions = sysResourceService.query4ShiroAuthorizationInfo(roleList, userExt);
                info = new SimpleAuthorizationInfo();
                info.setRoles(roles);
                info.setStringPermissions(permissions);
                ShiroAuthorizationInfoHelper.put(cacheName, info);
            }
            return info;
        } catch (Exception e) {
            throw new AuthorizationException(e.getMessage());
        }
    }
}
